Supabase 심화

심화 Supabase 기능 가이드: 통합 예시 및 실제 활용

1. 통합 애플리케이션 예시

문서 관리 시스템 구현 예시

// 문서 기본 구조 정의
interface Document {
 id: number;            // 문서 고유 ID
 title: string;         // 문서 제목
 content: string;       // 문서 내용
 version: number;       // 문서 버전 관리용 번호
 status: 'draft' | 'published' | 'archived';  // 문서 상태
 created_by: string;    // 작성자 ID
 created_at: string;    // 생성 시간
 updated_at: string;    // 최종 수정 시간
 tags: string[];        // 문서 태그 목록
}

class DocumentManager {
 private supabase: SupabaseClient;

 constructor() {
   // Supabase 클라이언트 초기화
   this.supabase = createClient(
     process.env.SUPABASE_URL!,
     process.env.SUPABASE_ANON_KEY!
   );
 }

 // 새 문서 생성 메서드
 async createDocument(title: string, content: string, tags: string[] = []): Promise<Document> {
   const { data, error } = await this.supabase
     .from('documents')
     .insert({
       title,
       content,
       version: 1,        // 최초 버전은 1로 설정
       status: 'draft',   // 초기 상태는 draft로 설정
       tags
     })
     .select()
     .single();

   if (error) throw error;
   return data;
 }

 // 문서 내용 업데이트 메서드
 async updateDocument(documentId: number, content: string): Promise<Document> {
   const { data, error } = await this.supabase
     .from('documents')
     .update({
       content,
       version: sql`version + 1`,  // 버전 자동 증가
       updated_at: new Date().toISOString()
     })
     .eq('id', documentId)
     .select()
     .single();

   if (error) throw error;
   return data;
 }

 // 실시간 문서 변경 구독 메서드
 subscribeToDocument(documentId: number, callback: (payload: any) => void) {
   return this.supabase
     .channel(`document:${documentId}`)
     .on('postgres_changes', 
       {
         event: '*',             // 모든 이벤트 감지
         schema: 'public',
         table: 'documents',
         filter: `id=eq.${documentId}`  // 특정 문서만 필터링
       },
       callback
     )
     .subscribe();
 }

 // 문서 첨부파일 업로드 메서드
 async uploadAttachment(documentId: number, file: File) {
   // 파일 경로 생성 (timestamp로 고유성 보장)
   const filePath = `documents/${documentId}/${Date.now()}_${file.name}`;
   
   const { error: uploadError } = await this.supabase
     .storage
     .from('document-attachments')    // document-attachments 버킷 사용
     .upload(filePath, file, {
       cacheControl: '3600',          // 1시간 캐시 설정
       upsert: true                   // 같은 경로 파일 덮어쓰기 허용
     });

   if (uploadError) throw uploadError;
   return filePath;
 }
}

2. 최적화 전략

데이터베이스 최적화

실시간 기능 최적화

스토리지 최적화

구현 예시

// 데이터베이스 최적화 설정 예시
/*
-- 전문 검색을 위한 인덱스 생성
CREATE INDEX idx_documents_search 
ON documents USING GIN (to_tsvector('english', content));

-- 문서 접근 제어를 위한 RLS 정책
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;

-- 조회 권한 정책 설정
CREATE POLICY "공개 문서 조회" ON documents
FOR SELECT USING (
 status = 'published' OR   -- 공개된 문서이거나
 auth.uid() = created_by   -- 작성자 본인인 경우
);
*/

// 실시간 기능 사용 예시
const subscribeToPublicDocuments = () => {
 supabase
   .channel('documents')
   .on('postgres_changes', 
     {
       event: 'UPDATE',              // 업데이트 이벤트만 구독
       schema: 'public',
       table: 'documents',
       filter: 'status=eq.published' // 공개 문서만 필터링
     },
     (payload) => {
       console.log('문서 업데이트:', payload);
     }
   )
   .subscribe();
};

// 스토리지 최적화 예시
const getOptimizedImageUrl = (path: string) => {
 // Supabase Storage 변환 기능을 사용한 이미지 최적화
 return supabase
   .storage
   .from('images')
   .getPublicUrl(path, {
     transform: {
       width: 800,         // 너비 조정
       height: 600,        // 높이 조정
       quality: 80         // 품질 설정
     }
   });
};

3. 보안 고려사항

인증 보안

데이터 보안

Edge Function 보안

구현 예시

// 인증 설정
const authConfig = {
  auth: {
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: true
  }
};

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, authConfig);

// 2FA 활성화 - Supabase 대시보드에서 설정
// RLS 정책 - SQL로 정책만 정의
// 백업 - Supabase 대시보드에서 스케줄링

// Edge Function에서 필요한 최소한의 보안 처리
export const myFunction = async (req: any) => {
  try {
    // 입력값 검증
    const { data } = req.body;
    if (!data) throw new Error('Invalid input');

    // 비즈니스 로직
    const result = await processData(data);
    
    return { data: result };
  } catch (error) {
    return { error: error.message };
  }
};